package com.study.springboot.common.mvc;

import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.SimpleDateFormatSerializer;
import com.alibaba.fastjson.serializer.ToStringSerializer;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.study.springboot.common.util.SpringUtil;
import org.hibernate.validator.HibernateValidator;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author Admin
 */
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {

    /* 自定义日期参数转换器，不配置该转换器使用 @DateTimeFormat(pattern = "yyyy-MM-dd")指定日期格式也可以 */
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new DateConverter());
    }

    /* 自定义登录拦截器 */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        InterceptorRegistration interceptorRegistration = registry.addInterceptor(SpringUtil.getBean(LoginInterceptor.class));
        interceptorRegistration.addPathPatterns("/**");
        interceptorRegistration.excludePathPatterns("/login");
    }

    /* 自定义日志过滤器，springboot提供了入口FilterRegistrationBean类来注册过滤器 */
    @Bean
    public FilterRegistrationBean registFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new LogFilter());
        registration.addUrlPatterns("/*");
        registration.setName(registration.getFilter().getClass().getName());
        registration.setOrder(1);
        return registration;
    }

    /*****************************自定义消息转换器**********************************************/
    /**
     * 自定义消息转换器---方式一
     * 注意：必须指定添加的fastJson消息转换器索引为0或者在默认的json消息转换器之前，因为会按照顺序筛选到第一个合适的应用。
     */
    /*@Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(0, getFastJsonHttpMessageConverter());
    }*/

    /**
     * 自定义消息转换器---方式二
     * 注意：必须指定添加的fastJson消息转换器索引为0或者在默认的json消息转换器之前，因为会按照顺序筛选到第一个合适的应用。
     */
   /* @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(0, getFastJsonHttpMessageConverter());
    }*/

    /*自定义消息转换器---方式三*/
    //@Bean
    public HttpMessageConverter customConverter() {
        return getFastJsonHttpMessageConverter();
    }

    /**
     * 自定义消息转换器---方式四
     * 注意：
     * 1.可以通过设置True、False决定是否添加默认消息转换器，默认true
     * 2.不知道为什么此时默认添加的StringHttpMessageConverter使用的是GBK，所以自定义了一个
     */
    @Bean
    public HttpMessageConverters customConverters() {
        return new HttpMessageConverters(getFastJsonHttpMessageConverter(),
                new StringHttpMessageConverter(Charset.forName("UTF-8")));
    }

    /*****************************解决跨域问题*****************************/
    /**
     * 解决跨域问题---方式一，本质上是一个拦截器！
     * 注意：假如同时配置了登录拦截器，用户没有登陆，此时登录拦截器先执行了，拦截了请求直接返回，
     * 并没有经过跨域处理的拦截器，那么此时浏览器不会响应未登录，因为浏览器拿到的响应头里并没有origin字段，
     * 所以此时浏览器会报跨域！
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins(CorsConfiguration.ALL)
                .allowedMethods(CorsConfiguration.ALL)
                .allowedHeaders(CorsConfiguration.ALL)
                .allowCredentials(true);
    }

    /* 解决跨域问题---方式二，本质上是一个过滤器 */
    // @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOrigin(CorsConfiguration.ALL);
        config.addAllowedMethod(CorsConfiguration.ALL);
        config.addAllowedHeader(CorsConfiguration.ALL);
        config.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource configurationSource = new UrlBasedCorsConfigurationSource();
        configurationSource.registerCorsConfiguration("/**", config);
        return new CorsFilter(configurationSource);
    }

    public static FastJsonHttpMessageConverter getFastJsonHttpMessageConverter() {
        FastJsonHttpMessageConverter fastJsonConverter = new FastJsonHttpMessageConverter();
        FastJsonConfig fjc = new FastJsonConfig();
        SerializeConfig mapping = new SerializeConfig();
        //注意：传入别的日期格式也能解析，这里定义的是响应解析成的日期格式
        mapping.put(Date.class, new SimpleDateFormatSerializer("yyyy-MM-dd HH:mm:ss"));
        mapping.put(Long.class, ToStringSerializer.instance);
        fjc.setSerializeConfig(mapping);
        fjc.setSerializerFeatures(
                SerializerFeature.DisableCircularReferenceDetect,
                SerializerFeature.WriteMapNullValue,
                SerializerFeature.WriteNullNumberAsZero,
                SerializerFeature.WriteNullStringAsEmpty);
        fastJsonConverter.setFastJsonConfig(fjc);
        List<MediaType> supportedMediaTypes = new ArrayList<>();
        supportedMediaTypes.add(MediaType.APPLICATION_JSON);
        supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
        supportedMediaTypes.add(MediaType.APPLICATION_ATOM_XML);
        supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
        supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
        supportedMediaTypes.add(MediaType.APPLICATION_PDF);
        supportedMediaTypes.add(MediaType.APPLICATION_RSS_XML);
        supportedMediaTypes.add(MediaType.APPLICATION_XHTML_XML);
        supportedMediaTypes.add(MediaType.APPLICATION_XML);
        supportedMediaTypes.add(MediaType.IMAGE_GIF);
        supportedMediaTypes.add(MediaType.IMAGE_JPEG);
        supportedMediaTypes.add(MediaType.IMAGE_PNG);
        fastJsonConverter.setSupportedMediaTypes(supportedMediaTypes);
        return fastJsonConverter;
    }

    /**
     * 参数校验开启快速失败模式
     */
    @Bean
    public Validator validator() {
        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                .configure()
                // 快速失败模式
                .failFast(true)
                .buildValidatorFactory();
        return validatorFactory.getValidator();
    }

}

